# Copyright 2018 Maco
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import sys
import types
import platform
import importlib
import tornado.ioloop
from tornado.options import options, define

ROOT_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__)))
if os.path.dirname(ROOT_PATH) not in sys.path:
    sys.path.insert(0, os.path.dirname(ROOT_PATH))

from blockchain.common.setting import setting
from blockchain.config import config
from blockchain.handler import base
from blockchain.common.util import printf


_PORT = config.get_int_opt('base', 'port', 8888)
_DEBUG = config.get_bool_opt('base', '_debug', False)
define('port', default=_PORT, help='run on the given port', type=int)
define('debug', default=_DEBUG, help='enable debug mode')


def register_module():
    '''
    Register module to application.
    '''
    handler_path = os.path.join(ROOT_PATH, 'handler')
    files = list()
    ignores = ['__init__', '__pycache__', 'base']
    for root, dirs, _files in os.walk(handler_path):
        cur_dir = root[len(handler_path) + 1:].replace('/', '.')
        if cur_dir in ignores:
            continue
        if cur_dir.split('.')[-1] in ignores:
            continue
        for _file in _files:
            if not os.path.isfile(os.path.join(root, _file)):
                continue
            if _file.split('.')[0] in ignores:
                continue
            if cur_dir:
                _module = '%s.%s' % (cur_dir, _file.split('.')[0])
            else:
                _module = _file.split('.')[0]
            if _module.startswith('.'):
                continue
            files.append(_module)

    modules = list(set(files))
    handlers = list()
    for mod in modules:
        module = importlib.import_module('handler.%s' % mod)
        find_modules(module, handlers)
    static_base = os.path.join(ROOT_PATH, 'static')
    _static_handler = (
        r"/static/(.*)", tornado.web.StaticFileHandler, {'path': static_base})
    handlers.append(_static_handler)
    return handlers


def find_modules(module, handlers):
    '''
    Load all modules
    '''
    assert isinstance(module, types.ModuleType)
    # check is BaseHandler handler
    cls_check = lambda cls: isinstance(
        cls, type) and issubclass(cls, base.BaseHandler)
    url_check = lambda cls: hasattr(cls, 'url_pattern') and cls.url_pattern
    for obj in dir(module):
        if obj.startswith('_'):
            continue
        cls = getattr(module, obj)
        if cls_check(cls) and url_check(cls):
            if hasattr(cls, 'url_prefix'):
                handlers.append((cls.url_prefix + cls.url_pattern, cls))
            else:
                handlers.append((cls.url_pattern, cls))
            #print((cls.url_pattern, cls))


def main():
    fork_start()
    # set_locale()
    # set_sys_parameters()
    # setting default error handler such as: 404
    settings = setting()
    settings['default_handler_class'] = base.ErrorHandler
    modules = register_module()
    tornado.options.parse_command_line()
    application = tornado.web.Application(modules, **settings)
    server = tornado.httpserver.HTTPServer(application, xheaders=True)
    if settings['debug'] or options.debug:
        server.listen(options.port)
    else:
        settings['autoreload'] = False
        sockets = tornado.netutil.bind_sockets(options.port)
        tornado.process.fork_processes(config.get_process_num())
        server.add_sockets(sockets)
    tornado.ioloop.IOLoop.current().start()


def fork_start():
    if platform.system() not in ('Linux', 'Darwin'):
        printf('Does not support window platforms.')
        sys.exit(1)
    try:
        pid = os.fork()
        if pid > 0:
            sys.exit(0)
    except OSError as err:
        printf('fork #1 failed: %d (%s)', err.errno, err.strerror)
        sys.exit(1)
    # create new session, sub process to become the first process
    os.setsid()
    # modify the working directory
    os.umask(0)
    try:
        pid = os.fork()
        if pid > 0:
            printf(f'[{pid}]Server start...')
            with open(ROOT_PATH + '/server.pid', 'w') as _file:
                _file.write(str(pid))
            sys.exit(0)
    except OSError as err:
        printf('fork #2 failed: %d (%s)', err.errno, err.strerror)
        sys.exit(1)

if __name__ == '__main__':
    main()
